#include "CommonConnectionPool.h"

#include <chrono>
#include <condition_variable>
#include <fstream>
#include <iostream>
#include <memory>
#include <mutex>
#include <thread>
#include <unordered_map>

#include "Connection.h"
#include "public.h"

// 线程安全的懒汉单例
ConnectionPool* ConnectionPool::getInstance() {
    // 静态局部变量 底层汇编自动lock和unlock 保证线程安全
    static ConnectionPool pool;  // 第一次运行时 才构造
    return &pool;
}

static std::unordered_map<std::string, std::string> ReadConfigFile(
    const std::string& filePath);

bool ConnectionPool::loadConfigFile(std::string configFilePath) {
    FILE* pf = fopen("mysql.cnf", "r");
    if (!pf) {
        LOG("Failed to open configuration file: " + configFilePath);
        return false;
    }
    std::unordered_map<std::string, std::string> configMap =
        ReadConfigFile(configFilePath);
    const char* keys[] = {"ip", "port", "username", "password", "dbname"};
    for (auto k : keys) {
        if ("" == configMap[k]) {
            LOG("Failed to configure:" + std::string(k) +
                ", check your configuration file.");
            return false;
        }
    }

    _ip = configMap["ip"];
    _port = std::stoi(configMap["port"]);
    _username = configMap["username"];
    _password = configMap["password"];
    _dbname = configMap["dbname"];

    // the following configuration can use the defaults
    _initSize =
        "" == configMap["initSize"] ? 10 : std::stoi(configMap["initSize"]);
    _maxSize =
        "" == configMap["maxSize"] ? 1024 : std::stoi(configMap["maxSize"]);
    _maxIdleTime = "" == configMap["maxIdleTime"]
                       ? 60
                       : std::stoi(configMap["maxIdleTime"]);
    _connectionTimeout = "" == configMap["connectionTimeout"]
                             ? 100
                             : std::stoi(configMap["connectionTimeout"]);
    return true;
}

std::unordered_map<std::string, std::string> ReadConfigFile(
    const std::string& filePath) {
    std::unordered_map<std::string, std::string> configMap;
    std::ifstream inputFile(filePath);
    std::string line;

    if (inputFile.is_open()) {
        while (std::getline(inputFile, line)) {
            // Skip empty lines or lines starting with '#'
            if (line.empty() || line[0] == '#') {
                continue;
            }

            // Find the position of the '=' character
            size_t equalPos = line.find('=');
            if (equalPos != std::string::npos) {
                // Extract the key and value
                std::string key = line.substr(0, equalPos);
                std::string value = line.substr(equalPos + 1);

                // Remove leading and trailing whitespaces from key and value
                key.erase(0, key.find_first_not_of(" \t"));
                key.erase(key.find_last_not_of(" \t") + 1);
                value.erase(0, value.find_first_not_of(" \t"));
                value.erase(value.find_last_not_of(" \t") + 1);

                // Add the key-value pair to the map
                configMap[key] = value;
            }
        }
        inputFile.close();
    } else {
        std::cerr << "Failed to open the configuration file: " << filePath
                  << std::endl;
    }

    return configMap;
}

ConnectionPool::ConnectionPool(const std::string& configFilePath) {
    if (!loadConfigFile(configFilePath)) {
        LOG("Failed to construct connection pool.");
        return;
    }

    // 创建初始连接
    for (int i = 0; i < _initSize; ++i) {
        Connection* conn =
            new Connection(_ip, _port, _username, _password, _dbname);
        conn->refreshIdleStartPoint();  // 刷新起始空闲时间点
        _connectionQueue.push(conn);
        _connCnt++;
    }

    // 启动一个新的线程， 作为连接的生产者
    // 为了方便线程函数中访问连接池的成员，将该线程函数定义为线程池的成员函数
    // 线程函数必须定义为c函数接口，不能是类的成员函数，故使用绑定器绑定当前对象
    std::thread producer(
        std::bind(&ConnectionPool::produceConnectionTask, this));
    producer.detach();  // 分离线程

    // 启动一个新的定时线程
    // 扫描连接池中的所有连接，释放超出最长空闲时间的连接
    std::thread releaseThread(
        std::bind(&ConnectionPool::releaseIdleConnection, this));
    releaseThread.detach();  // 分离线程
}

// 生产者
void ConnectionPool::produceConnectionTask() {
    for (;;) {
        // 互斥访问 连接队列
        std::unique_lock<std::mutex> unique_lock(_queueMtx);
        while (!_connectionQueue.empty()) {
            _cv.wait(unique_lock);  // 队列不空 生产线程等待 释放锁
        }  // 生产者被消费者唤醒, 对锁竞争，重新判断连接队列是否为空

        if (_connCnt < _maxSize) {
            Connection* conn =
                new Connection(_ip, _port, _username, _password, _dbname);

            conn->refreshIdleStartPoint();  // 刷新起始空闲时间点
            _connectionQueue.push(conn);
            _connCnt++;
        }  // 连接数量达到最大值不创建，等待消费者归还连接

        _cv.notify_all();  // 通知消费者使用连接
    }
}

// 消费者
std::shared_ptr<Connection> ConnectionPool::getConnection() {
    // 互斥访问
    std::unique_lock<std::mutex> unique_lock(_queueMtx);
#if 0
    // 一次判断
    if (_connectionQueue.empty()) {
        // 不是死等，唤醒之后，继续判断一次
        _cv.wait_for(unique_lock,
                     std::chrono::milliseconds(_connectionTimeout));
        // 二次判断
        // 一种可能的情况是，消费者没有等待到超时时间，被唤醒，但是锁没有拿到，又为空了，需改进
        if (_connectionQueue.empty()) {
            LOG("Failed to get Connection from ConnectionPool.");
            return nullptr;
        }
    }
#endif

    // 改进，保证消费者等待足够超时时间
    while (_connectionQueue.empty()) {
        if (std::cv_status::timeout ==
            _cv.wait_for(unique_lock,
                         std::chrono::milliseconds(_connectionTimeout))) {
            // 等待了足够超时时间,自动醒来
            if (_connectionQueue.empty()) {  // 仍旧为空，不继续尝试获取连接
                LOG("Failed to get Connection from ConnectionPool.");
                return nullptr;
            }
        }  // 没有等待足够超时时间，被唤醒，继续尝试
    }

    // 注意：shared_ptr
    // 智能指针析构时，会把connection资源直接delete，相当于调用connection的析构函数，那么连接就被关闭了
    // 我们的初衷是为了回收连接，所以需要自定义删除器，将连接归还到队列中
    std::shared_ptr<Connection> conn(
        _connectionQueue.front(), [&](Connection* pconn) -> void {
            // 必须考虑线程安全
            std::unique_lock<std::mutex> lock(_queueMtx);
            pconn->refreshIdleStartPoint();
            _connectionQueue.push(pconn);
        });
    _connectionQueue.pop();
    _cv.notify_all();

    return conn;
}

void ConnectionPool::releaseIdleConnection() {
    for (;;) {
        // 通过sleep模拟定时效果
        std::this_thread::sleep_for(std::chrono::seconds(_maxIdleTime));

        // 扫描连接池中的所有连接，释放超出最长空闲时间的连接
        std::unique_lock<std::mutex> lock(_queueMtx);  // 保证线程安全
        while (_connCnt > _initSize) {
            Connection* p = _connectionQueue.front();
            if (p->getIdleTime() >= (_maxIdleTime * 1000)) {
                _connectionQueue.pop();
                _connCnt--;
                delete p;
            } else {
                // 对头的连接没有超出最长空闲，则其余连接均不会超过
                break;
            }
        }
    }
}
